﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace King.Engine.GameObjects.Hud.Menu
{
    /// <summary>
    /// A tree of menu items
    /// </summary>
    public class MenuTree : IMenuNode
    {

        #region Fields

        private List<IMenuNode> _nodes;
        private int _selectedIndex;

        #endregion


        #region Properties

        public MenuTree Parent { get; set; }
        
        public string Text
        {
            get;
            set;
        }

        public string Description
        {
            get;
            set;
        }

        public IMenuNode SelectedNode
        {
            get
            {
                return _nodes[_selectedIndex];
            }
        }
        
        public string[] Items
        {
            get
            {
                if (SubMenuIsOpen)
                {
                    return (SelectedNode as MenuTree).Items;
                }
                else
                {
                    return _nodes.Select(n => n.Text)
                                 .ToArray();
                }
            }
        }

        public string ItemDescription
        {
            get
            {
                return SelectedNode.Description;
            }
        }

        internal int SelectedIndex
        {
            get
            {
                return (SubMenuIsOpen) ? (SelectedNode as MenuTree).SelectedIndex : _selectedIndex;
            }

            private set
            {
                _selectedIndex = value;
            }
        }

        internal string CurrentText
        {
            get
            {
                return (SubMenuIsOpen) ? (SelectedNode as MenuTree).CurrentText : this.Text;
            }
        }

        internal string CurrentItemDescription
        {
            get
            {
                return (SubMenuIsOpen) ? (SelectedNode as MenuTree).CurrentItemDescription : this.ItemDescription;
            }
        }
        
        private bool SubMenuIsOpen;
        
        #endregion


        #region Constructor

        /// <summary>
        /// Create a Menu Tree with the given name
        /// </summary>
        public MenuTree(string text)
            :this(text, String.Empty)
        {

        }

        public MenuTree(string text, string description)
        {
            Text = text;
            Description = description;
            _nodes = new List<IMenuNode>();
            SelectedIndex = 0;
        }
            
        #endregion


        #region Public Methods

        /// <summary>
        /// Add a menu item that allows the player to scroll through a range of values.
        /// </summary>
        /// <param name="listName">Name of the list</param>
        /// <param name="items">List of selectable values</param>
        /// <param name="itemSelected">Callback method for when a new item has been selected</param>
        public void Add(string listName, string[] items, Action<string> itemSelected)
        {
            Add(listName, items, itemSelected, 0);
        }
        
        /// <summary>
        /// Add a menu item that allows the player to scroll through a range of values.
        /// </summary>
        /// <param name="listName">Name of the list</param>
        /// <param name="items">List of selectable values</param>
        /// <param name="itemSelected">Callback method for when a new item has been selected</param>
        /// <param name="index">The index of the first value to be selected</param>
        public void Add(string listName, string[] items, Action<string> itemSelected, int index)
        {
            ListNode node = new ListNode(listName, itemSelected, items, index);
            Add(node);
        }

        /// <summary>
        /// Add a menu item that, when selected, calls the given action
        /// </summary>
        public void Add(string itemName, Action action)
        {
            ActionNode node = new ActionNode(itemName, action);
            Add(node);
        }

        /// <summary>
        /// Add a submenu
        /// </summary>
        public void Add(MenuTree node)
        {
            Add(node as IMenuNode);
        }

        internal void Add(IMenuNode node)
        {
            _nodes.Add(node);
            node.Parent = this;
        }

        public void OnInput(MenuControl control)
        {
            if (SubMenuIsOpen)
            {
                SelectedNode.OnInput(control);
            }
            else
            {
                switch (control)
                {
                    case MenuControl.Up:
                        SelectedIndex = MathUtil.TrueMod(SelectedIndex - 1, _nodes.Count);
                        break;

                    case MenuControl.Down:
                        SelectedIndex = MathUtil.TrueMod(SelectedIndex + 1, _nodes.Count);
                        break;

                    case MenuControl.Back:
                        if (Parent != null)
                        {
                            Parent.SubMenuIsOpen = false;
                        }
                        break;

                    case MenuControl.Select:
                        if (SelectedNode is MenuTree)
                        {
                            SubMenuIsOpen = true;
                        }
                        else
                        {
                            SelectedNode.OnInput(control);
                        }
                        break;

                    default:
                        SelectedNode.OnInput(control);
                        break;
                }
            }
        }

        public IEnumerator<IMenuNode> GetEnumerator()
        {
            return (SubMenuIsOpen) ? SelectedNode.GetEnumerator() : _nodes.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (SubMenuIsOpen) ? SelectedNode.GetEnumerator() : _nodes.GetEnumerator();
        }

        #endregion

    }
}
